#include "MyQsciDoc.h"

struct SortSelectInfoInPositionOrder {
    bool operator() (SelectInfo& l, SelectInfo& r) {
        return (l.leftPos < r.leftPos);
    }
};

struct SortSelectInfoInSelectOrder {
    bool operator() (SelectInfo& l, SelectInfo& r) {
        return (l.order < r.order);
    }
};

SelectInfo::SelectInfo(intptr_t lPos, intptr_t rPos, intptr_t order, SelectDirection dir, intptr_t vAnchorNbSpc, intptr_t vCaretNbSpc)
    : leftPos(lPos), rightPos(rPos), order(order), direction(dir), nbVirtualAnchorSpc(vAnchorNbSpc), nbVirtualCaretSpc(vCaretNbSpc) {};

bool SelectInfo::Valid() const {
    return (order >= 0 && leftPos >= 0 && rightPos >= 0 && leftPos < rightPos);
};


MyQsciDoc::MyQsciDoc(QWidget* parent, std::function<QsciScintilla* ()> func)
	: QWidget(parent), parentWidget(parent), getCurrentEditFunc(func)
{
}

MyQsciDoc::~MyQsciDoc()
{
}

SelectInfos MyQsciDoc::GetMultipleSelections(SelectSortType sortType)
{
    SelectInfos colInfos;
    if (!Valid()) {
        return colInfos;
    }
    REF_EDIT(edit);
    if (edit.SendScintilla(SCI_GETSELECTIONS) >= 1) {
        intptr_t nbSel = edit.SendScintilla(SCI_GETSELECTIONS);

        for (int i = 0; i < nbSel; ++i) {
            intptr_t absPosSelStartPerLine = edit.SendScintilla(SCI_GETSELECTIONNANCHOR, i);
            intptr_t absPosSelEndPerLine = edit.SendScintilla(SCI_GETSELECTIONNCARET, i);
            intptr_t nbVirtualAnchorSpc = edit.SendScintilla(SCI_GETSELECTIONNANCHORVIRTUALSPACE, i);
            intptr_t nbVirtualCaretSpc = edit.SendScintilla(SCI_GETSELECTIONNCARETVIRTUALSPACE, i);
            if (absPosSelStartPerLine == absPosSelEndPerLine && edit.SendScintilla(SCI_SELECTIONISRECTANGLE)) {
                auto dir = nbVirtualAnchorSpc < nbVirtualCaretSpc ? SEL_DIR_L2R : SEL_DIR_R2L;
                colInfos.push_back(SelectInfo(absPosSelStartPerLine, absPosSelEndPerLine, i, dir, nbVirtualAnchorSpc, nbVirtualCaretSpc));
            } else if (absPosSelStartPerLine > absPosSelEndPerLine) {
                colInfos.push_back(SelectInfo(absPosSelEndPerLine, absPosSelStartPerLine, i, SEL_DIR_R2L, nbVirtualAnchorSpc, nbVirtualCaretSpc));
            } else {
                colInfos.push_back(SelectInfo(absPosSelStartPerLine, absPosSelEndPerLine, i, SEL_DIR_L2R, nbVirtualAnchorSpc, nbVirtualCaretSpc));
            }
        }
    }
    if (sortType == SEL_SORT_BY_POSITION) {
        std::sort(colInfos.begin(), colInfos.end(), SortSelectInfoInPositionOrder());
    } else if (sortType == SEL_SORT_BY_SELECT_ORDER) {
        std::sort(colInfos.begin(), colInfos.end(), SortSelectInfoInSelectOrder());
    }
    return colInfos;
}

void MyQsciDoc::SelectMultipleRanges(const SelectInfos& selections)
{
    if (!Valid()) {
        return;
    }
    REF_EDIT(edit);
    edit.SendScintilla(SCI_CLEARSELECTIONS);
    size_t addCnt = 0;
    SelectInfos colInfos = selections;
    int endPos = 0;
    std::sort(colInfos.begin(), colInfos.end(), SortSelectInfoInPositionOrder());
    for (auto& column : colInfos) {
        if (!column.Valid()) {
            continue;
        }
        endPos = column.rightPos;
        edit.SendScintilla(SCI_ADDSELECTION, column.leftPos, column.rightPos);
        addCnt++;
    }
    if (addCnt != 0) {
        edit.SendScintilla(SCI_DROPSELECTIONN, 0);
        int ln, idx;
        edit.lineIndexFromPosition(endPos, &ln, &idx);
        if (ln >= 0) {
            edit.SendScintilla(SCI_ENSUREVISIBLEENFORCEPOLICY, ln);
        } else {
            edit.SendScintilla(SCI_SCROLLCARET);
        }
    }
}

void MyQsciDoc::SetText(const QByteArray& text)
{
    if (!Valid()) {
        return;
    }
    REF_EDIT(edit);
    bool ro = edit.isReadOnly();
    if (ro) {
        edit.setReadOnly(false);
    }
    edit.SendScintilla(SCI_SET_UTF8_TEXT, text.length(), text.constData());
    edit.setReadOnly(ro);
}

QByteArray MyQsciDoc::GetText()
{
    if (!Valid()) {
        return QByteArray();
    }
    REF_EDIT(edit);
    size_t buflen = edit.SendScintilla(SCI_GETTEXTLENGTH) + 1;
    char* buf = new char[buflen];
    edit.SendScintilla(SCI_GETTEXT, buflen, buf);
    QByteArray bytes(buf, buflen - 1);
    delete[] buf;
    return bytes;
}

NewLineType MyQsciDoc::GetNewLineType()
{
    if (!Valid()) {
        return NEW_LINE_LF;
    }
    REF_EDIT(edit);
    switch (edit.eolMode()) {
        case QsciScintilla::EolWindows:
            return NEW_LINE_CR_LF;
        case QsciScintilla::EolUnix:
            return NEW_LINE_LF;
        case QsciScintilla::EolMac:
            return NEW_LINE_CR;
        default:
            break;
    }
    return NEW_LINE_LF;
}

QsciScintilla* MyQsciDoc::operator->()
{
    if (getCurrentEditFunc == nullptr) {
        return nullptr;
    }
    return getCurrentEditFunc();
}

bool MyQsciDoc::Valid()
{
    if (getCurrentEditFunc == nullptr) {
        return false;
    }
    return getCurrentEditFunc() != nullptr;
}